package com.zeratul.common.pool.utils;

import com.zeratul.common.core.constant.PoolConstants;
import com.zeratul.common.core.utils.spring.SpringUtils;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import java.util.Objects;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.Delayed;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;

/**
 * 延迟消息工具类
 */
@SuppressWarnings({"rawtypes", "unchecked"})
@Slf4j
public class DelayMsgUtils {

    /**
     * 创建延迟队列
     */
    private static DelayQueue<MyDelayTask> delayQueue = new DelayQueue<>();

    /**
     * 消费延迟队列
     */
    static {
        //获取线程池
        Executor executor = SpringUtils.getBean(PoolConstants.DELAY_POOL);
        Objects.requireNonNull(executor).execute(() -> {
            while (true) {
                try {
                    //获取延迟任务
                    MyDelayTask task = delayQueue.take();
                    //执行延迟任务
                    Object msg = task.getMsg();
                    //执行消费
                    Consumer consumer = task.consumer;
                    consumer.accept(msg);
                } catch (Exception e) {
                    log.error("【延迟队列】消息处理失败！", e);
                }
            }
        });

    }

    /**
     * 添加延迟任务
     */
    public static <T> void addDelayTask(T msg, Long delayTime, Consumer<T> consumer) {
        delayQueue.add(new MyDelayTask<>(msg, delayTime, consumer));
    }

    /**
     * 创建延迟任务包装类
     */
    @Data
    private static class MyDelayTask<T> implements Delayed {

        /**
         * 延迟消息
         */
        private T msg;

        /**
         * 处理逻辑
         */
        private Consumer<T> consumer;

        /**
         * 消息的到期时间
         */
        private long endTime;

        /**
         * 构造方法
         * @param msg - 消息内容
         * @param delayTime - 延迟的时间，单位ms
         */
        public MyDelayTask(T msg, Long delayTime, Consumer<T> consumer) {
            this.msg = msg;
            this.consumer = consumer;
            //计算到期的时间
            this.endTime = System.currentTimeMillis() + delayTime;
        }

        /**
         * 到期时间，该方法返回 大于0表示未到期 小于0表示已经到期
         * @param unit the time unit
         * @return
         */
        @Override
        public long getDelay(TimeUnit unit) {
            //设置过期时间
            long diff = endTime - System.currentTimeMillis();
            return unit.convert(diff, TimeUnit.MILLISECONDS);
        }

        /**
         * 延迟消息的比较方法
         * @param o
         * @return
         */
        @Override
        public int compareTo(Delayed o) {
            return (int) (endTime - ((MyDelayTask)o).getEndTime());
        }
    }
}
